package com.zijidelu.luframework.utils.lang.reflect;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 包扫描工具类，用于获取指定包下的所有类。
 *
 * @author ZIJIDELU
 * @datetime 2025/10/18 16:30
 */
public class PackageScanUtil {

    /**
     * 获取指定包下的所有类
     *
     * @param packageName 包名
     * @return 类集合
     */
    public static Set<Class<?>> scanClasses(String packageName) {
        return scanClasses(packageName, null);
    }

    /**
     * 获取指定包下的所有类，并进行过滤
     *
     * @param packageName 包名
     * @param filter      过滤条件
     * @return 类集合
     */
    public static Set<Class<?>> scanClasses(String packageName, Predicate<Class<?>> filter) {
        if (StrUtil.isBlank(packageName)) {
            return Collections.emptySet();
        }

        Set<Class<?>> classes = ClassUtil.scanPackage(packageName);

        if (filter != null) {
            classes = classes.stream()
                    .filter(filter)
                    .collect(Collectors.toSet());
        }

        return classes;
    }

    /**
     * 获取指定包下带有指定注解的所有类
     *
     * @param packageName     包名
     * @param annotationClass 注解类
     * @return 类集合
     */
    public static Set<Class<?>> scanClassesWithAnnotation(String packageName, Class<? extends Annotation> annotationClass) {
        return scanClasses(packageName, clazz -> clazz.isAnnotationPresent(annotationClass));
    }

    /**
     * 获取指定包下继承指定父类的所有子类
     *
     * @param packageName 包名
     * @param superClass  父类
     * @return 类集合
     */
    public static Set<Class<?>> scanSubClasses(String packageName, Class<?> superClass) {
        return scanClasses(packageName, clazz -> !clazz.equals(superClass) && superClass.isAssignableFrom(clazz));
    }

    /**
     * 获取指定包下实现指定接口的所有类
     *
     * @param packageName    包名
     * @param interfaceClass 接口类
     * @return 类集合
     */
    public static Set<Class<?>> scanClassesWithInterface(String packageName, Class<?> interfaceClass) {
        if (!interfaceClass.isInterface()) {
            return Collections.emptySet();
        }

        return scanClasses(packageName, clazz -> {
            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class<?> iface : interfaces) {
                if (iface.equals(interfaceClass)) {
                    return true;
                }
            }
            return false;
        });
    }

    /**
     * 获取指定包下的所有非抽象类
     *
     * @param packageName 包名
     * @return 类集合
     */
    public static Set<Class<?>> scanConcreteClasses(String packageName) {
        return scanClasses(packageName, clazz -> !Modifier.isAbstract(clazz.getModifiers()));
    }

    /**
     * 获取指定包下的所有接口
     *
     * @param packageName 包名
     * @return 接口集合
     */
    public static Set<Class<?>> scanInterfaces(String packageName) {
        return scanClasses(packageName, Class::isInterface);
    }

    /**
     * 获取指定包下的所有枚举类
     *
     * @param packageName 包名
     * @return 枚举类集合
     */
    public static Set<Class<?>> scanEnums(String packageName) {
        return scanClasses(packageName, Class::isEnum);
    }

    /**
     * 获取指定包下的所有注解类
     *
     * @param packageName 包名
     * @return 注解类集合
     */
    public static Set<Class<?>> scanAnnotations(String packageName) {
        return scanClasses(packageName, Class::isAnnotation);
    }

    /**
     * 获取指定包下的所有类的类名
     *
     * @param packageName 包名
     * @return 类名集合
     */
    public static Set<String> scanClassNames(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        return classes.stream()
                .map(Class::getName)
                .collect(Collectors.toSet());
    }

    /**
     * 获取指定包下的所有简单类名
     *
     * @param packageName 包名
     * @return 简单类名集合
     */
    public static Set<String> scanSimpleClassNames(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        return classes.stream()
                .map(Class::getSimpleName)
                .collect(Collectors.toSet());
    }

    /**
     * 获取指定包下的所有类，并按包名分组
     *
     * @param packageName 包名
     * @return 按包名分组的类集合
     */
    public static Map<String, Set<Class<?>>> scanClassesGroupByPackage(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        return classes.stream()
                .collect(Collectors.groupingBy(
                        clazz -> clazz.getPackage().getName(),
                        Collectors.toSet()
                ));
    }

    /**
     * 获取指定包下的所有类，并按父类分组
     *
     * @param packageName 包名
     * @return 按父类分组的类集合
     */
    public static Map<Class<?>, Set<Class<?>>> scanClassesGroupBySuperClass(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        Map<Class<?>, Set<Class<?>>> result = new HashMap<>();

        for (Class<?> clazz : classes) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                result.computeIfAbsent(superClass, k -> new HashSet<>()).add(clazz);
            }
        }

        return result;
    }

    /**
     * 获取指定包下的所有类，并按实现的接口分组
     *
     * @param packageName 包名
     * @return 按接口分组的类集合
     */
    public static Map<Class<?>, Set<Class<?>>> scanClassesGroupByInterface(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        Map<Class<?>, Set<Class<?>>> result = new HashMap<>();

        for (Class<?> clazz : classes) {
            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class<?> iface : interfaces) {
                result.computeIfAbsent(iface, k -> new HashSet<>()).add(clazz);
            }
        }

        return result;
    }

    /**
     * 获取指定包下的所有类，并按注解分组
     *
     * @param packageName 包名
     * @return 按注解分组的类集合
     */
    public static Map<Class<? extends Annotation>, Set<Class<?>>> scanClassesGroupByAnnotation(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        Map<Class<? extends Annotation>, Set<Class<?>>> result = new HashMap<>();

        for (Class<?> clazz : classes) {
            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation : annotations) {
                result.computeIfAbsent(annotation.annotationType(), k -> new HashSet<>()).add(clazz);
            }
        }

        return result;
    }

    /**
     * 获取指定包下的所有类，并按修饰符分组
     *
     * @param packageName 包名
     * @return 按修饰符分组的类集合
     */
    public static Map<String, Set<Class<?>>> scanClassesGroupByModifier(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        Map<String, Set<Class<?>>> result = new HashMap<>();

        for (Class<?> clazz : classes) {
            int modifiers = clazz.getModifiers();
            String modifierStr = Modifier.toString(modifiers);
            result.computeIfAbsent(modifierStr, k -> new HashSet<>()).add(clazz);
        }

        return result;
    }

    /**
     * 获取指定包下的所有类，并按类型分组
     *
     * @param packageName 包名
     * @return 按类型分组的类集合
     */
    public static Map<String, Set<Class<?>>> scanClassesGroupByType(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        Map<String, Set<Class<?>>> result = new HashMap<>();

        for (Class<?> clazz : classes) {
            if (clazz.isInterface()) {
                result.computeIfAbsent("Interface", k -> new HashSet<>()).add(clazz);
            } else if (clazz.isEnum()) {
                result.computeIfAbsent("Enum", k -> new HashSet<>()).add(clazz);
            } else if (clazz.isAnnotation()) {
                result.computeIfAbsent("Annotation", k -> new HashSet<>()).add(clazz);
            } else if (Modifier.isAbstract(clazz.getModifiers())) {
                result.computeIfAbsent("Abstract Class", k -> new HashSet<>()).add(clazz);
            } else {
                result.computeIfAbsent("Concrete Class", k -> new HashSet<>()).add(clazz);
            }
        }

        return result;
    }

    /**
     * 获取指定包下的所有类，并按名称前缀分组
     *
     * @param packageName 包名
     * @return 按名称前缀分组的类集合
     */
    public static Map<String, Set<Class<?>>> scanClassesGroupByNamePrefix(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        Map<String, Set<Class<?>>> result = new HashMap<>();

        for (Class<?> clazz : classes) {
            String className = clazz.getSimpleName();
            if (StrUtil.isNotBlank(className)) {
                String prefix = className.substring(0, 1);
                result.computeIfAbsent(prefix, k -> new HashSet<>()).add(clazz);
            }
        }

        return result;
    }

    /**
     * 获取指定包下的所有类，并按名称后缀分组
     *
     * @param packageName 包名
     * @return 按名称后缀分组的类集合
     */
    public static Map<String, Set<Class<?>>> scanClassesGroupByNameSuffix(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        Map<String, Set<Class<?>>> result = new HashMap<>();

        for (Class<?> clazz : classes) {
            String className = clazz.getSimpleName();
            if (StrUtil.isNotBlank(className)) {
                String suffix = className.substring(className.length() - 1);
                result.computeIfAbsent(suffix, k -> new HashSet<>()).add(clazz);
            }
        }

        return result;
    }

    /**
     * 获取指定包下的所有类，并按名称长度分组
     *
     * @param packageName 包名
     * @return 按名称长度分组的类集合
     */
    public static Map<Integer, Set<Class<?>>> scanClassesGroupByNameLength(String packageName) {
        Set<Class<?>> classes = scanClasses(packageName);
        Map<Integer, Set<Class<?>>> result = new HashMap<>();

        for (Class<?> clazz : classes) {
            String className = clazz.getSimpleName();
            if (StrUtil.isNotBlank(className)) {
                int length = className.length();
                result.computeIfAbsent(length, k -> new HashSet<>()).add(clazz);
            }
        }

        return result;
    }

    /**
     * 获取指定包下的所有类，并按名称模式匹配分组
     *
     * @param packageName 包名
     * @param pattern     名称模式
     * @return 按名称模式匹配分组的类集合
     */
    public static Map<Boolean, Set<Class<?>>> scanClassesGroupByNamePattern(String packageName, String pattern) {
        Set<Class<?>> classes = scanClasses(packageName);
        Map<Boolean, Set<Class<?>>> result = new HashMap<>();
        result.put(true, new HashSet<>());
        result.put(false, new HashSet<>());

        for (Class<?> clazz : classes) {
            String className = clazz.getSimpleName();
            if (StrUtil.isNotBlank(className)) {
                boolean matches = className.matches(pattern);
                result.get(matches).add(clazz);
            }
        }

        return result;
    }

    /**
     * 获取指定包下的所有类，并按自定义条件分组
     *
     * @param packageName 包名
     * @param classifier  分类器
     * @return 按自定义条件分组的类集合
     */
    public static <T> Map<T, Set<Class<?>>> scanClassesGroupByClassifier(String packageName, java.util.function.Function<Class<?>, T> classifier) {
        Set<Class<?>> classes = scanClasses(packageName);
        return classes.stream()
                .collect(Collectors.groupingBy(classifier, Collectors.toSet()));
    }

    /**
     * 获取指定包下的所有类，并按自定义条件过滤
     *
     * @param packageName 包名
     * @param filter      过滤条件
     * @return 过滤后的类集合
     */
    public static Set<Class<?>> scanClassesWithFilter(String packageName, Predicate<Class<?>> filter) {
        return scanClasses(packageName, filter);
    }

    /**
     * 获取指定包下的所有类，并按自定义条件过滤和分组
     *
     * @param packageName 包名
     * @param filter      过滤条件
     * @param classifier  分类器
     * @return 过滤和分组后的类集合
     */
    public static <T> Map<T, Set<Class<?>>> scanClassesWithFilterAndGroupByClassifier(String packageName,
                                                                                      Predicate<Class<?>> filter,
                                                                                      java.util.function.Function<Class<?>, T> classifier) {
        Set<Class<?>> classes = scanClasses(packageName, filter);
        return classes.stream()
                .collect(Collectors.groupingBy(classifier, Collectors.toSet()));
    }

    /**
     * 获取指定包下的所有类，并按多个条件过滤
     *
     * @param packageName 包名
     * @param filters     过滤条件数组
     * @return 过滤后的类集合
     */
    public static Set<Class<?>> scanClassesWithFilters(String packageName, Predicate<Class<?>>... filters) {
        if (filters == null || filters.length == 0) {
            return scanClasses(packageName);
        }

        return scanClasses(packageName, clazz -> {
            for (Predicate<Class<?>> filter : filters) {
                if (!filter.test(clazz)) {
                    return false;
                }
            }
            return true;
        });
    }

    /**
     * 获取指定包下的所有类，并按多个条件过滤和分组
     *
     * @param packageName 包名
     * @param filters     过滤条件数组
     * @param classifier  分类器
     * @return 过滤和分组后的类集合
     */
    public static <T> Map<T, Set<Class<?>>> scanClassesWithFiltersAndGroupByClassifier(String packageName,
                                                                                       Predicate<Class<?>>[] filters,
                                                                                       java.util.function.Function<Class<?>, T> classifier) {
        Set<Class<?>> classes = scanClassesWithFilters(packageName, filters);
        return classes.stream()
                .collect(Collectors.groupingBy(classifier, Collectors.toSet()));
    }

    /**
     * 获取指定包下的所有类，并按多个条件过滤和分组，支持并行处理
     *
     * @param packageName 包名
     * @param filters     过滤条件数组
     * @param classifier  分类器
     * @param parallel    是否并行处理
     * @return 过滤和分组后的类集合
     */
    public static <T> Map<T, Set<Class<?>>> scanClassesWithFiltersAndGroupByClassifier(String packageName,
                                                                                       Predicate<Class<?>>[] filters,
                                                                                       java.util.function.Function<Class<?>, T> classifier,
                                                                                       boolean parallel) {
        Set<Class<?>> classes = scanClassesWithFilters(packageName, filters);

        if (parallel) {
            return classes.parallelStream()
                    .collect(Collectors.groupingByConcurrent(classifier, Collectors.toSet()));
        } else {
            return classes.stream()
                    .collect(Collectors.groupingBy(classifier, Collectors.toSet()));
        }
    }
}